use crate::model::ProcessDefinition;
use crate::model::process_definition::Process;
use crate::model::process_definition::SequenceFlow;
use crate::model::process_definition::Node;
use quick_xml::Reader;
use quick_xml::events::Event;
use std::str;
use std::collections::HashMap;

pub struct WorkFlow;

impl WorkFlow {
    pub fn parse_bpmn_xml(xml: &str) -> Result<ProcessDefinition, String> {
        let mut reader = Reader::from_str(xml);
        reader.config_mut().trim_text(true);
        let mut buf = Vec::new();
        let mut process_definition = ProcessDefinition {
            id: String::new(),
            name: None,
            process: Process {
                id: String::new(),
                name: None,
                nodes: Vec::new(),
                sequenceFlow: Vec::new(),
            },
        };

        loop {
            match reader.read_event_into(&mut buf) {
                Err(e) => return Err(format!("Error at position {}: {:?}", reader.error_position(), e)),
                Ok(Event::Eof) => break,

                Ok(Event::Start(e)) | Ok(Event::Empty(e)) => {
                    // println!("Event: {:?}", e.name());
                    match e.name().as_ref() {
                        b"bpmn:definitions" => {
                            let id = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"id"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            let name = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"name"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            process_definition.id = id.unwrap_or_default();
                            process_definition.name = name;
                        },
                        b"bpmn:process" => {
                            let id = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"id"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            let name = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"name"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            process_definition.process.id = id.unwrap_or_default();
                            process_definition.process.name = name;
                        },
                        b"bpmn:startEvent" | b"bpmn:endEvent" | b"bpmn:task" => {
                            let id = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"id"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            let name = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"name"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            let event_type = if e.name().as_ref() == b"bpmn:startEvent" {
                                "startEvent"
                            } else if e.name().as_ref() == b"bpmn:endEvent" {
                                "endEvent"
                            } else {
                                "task"
                            };
                            let node = Node {
                                id: id.unwrap_or_default(),
                                name,
                                type_: event_type.to_string(),
                            };
                            process_definition.process.nodes.push(node);
                        },
                        b"bpmn:sequenceFlow" => {
                            let id = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"id"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            let name = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"name"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            let source_ref = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"sourceRef"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            let target_ref = e.attributes()
                                .find(|a| a.as_ref().unwrap().key ==  quick_xml::name::QName(b"targetRef"))
                                .and_then(|a| a.ok())
                                .map(|a| str::from_utf8(&a.value).unwrap_or_default().to_string());
                            let sequence_flow = SequenceFlow {
                                id: id.unwrap_or_default(),
                                name: name,
                                sourceRef: source_ref.unwrap_or_default(),
                                targetRef: target_ref.unwrap_or_default(),
                            };
                            process_definition.process.sequenceFlow.push(sequence_flow);
                        },
                        _ => (),
                    }
                },
                Ok(Event::Text(_)) => {
                    // 处理文本内容，如果需要的话
                },
                _ => (),
            }
            buf.clear();
        }

        Ok(process_definition)
    }
}